#include "GameCamera.h"
#include <OgreRenderWindow.h>

//-------------------------------------------------------------------------------------
GameCamera::GameCamera(Ogre::Camera *camera, Ogre::RenderWindow *window, float minDistance, float maxDistance)
{
	mpCamera = camera;
	mpViewport = window->addViewport(mpCamera);
	mInitialized = false;
	mReseted = true;
	mfMinDistance = minDistance;
	mfMaxDistance = maxDistance;
	mfLastDistance = mfMinDistance;

	//mLookAtZList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	//mLookAtZList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
}
//-------------------------------------------------------------------------------------
GameCamera::~GameCamera(void)
{
}
//-------------------------------------------------------------------------------------
bool GameCamera::init(MoveableObjectPtr object)
{
	mpTrackedObject = object;

	// Calculate and set position
	Ogre::Vector3 pos = object->getNode()->getPosition();
	pos = pos + mfMinDistance * object->getNode()->getOrientation().zAxis();
	mpCamera->setPosition(pos);

	mpCamera->lookAt(object->getNode()->getPosition());
	mpCamera->setNearClipDistance(3.0f);

	mpViewport->setBackgroundColour(Ogre::ColourValue(0.0f, 0.0f, 0.0f));
	
	mpCamera->setAspectRatio(Ogre::Real(mpViewport->getActualWidth()) / Ogre::Real(mpViewport->getActualHeight()));
	
	mInitialized = true;

	return mInitialized;
}
//-------------------------------------------------------------------------------------
void GameCamera::update(const unsigned long elapsedTime)
{
	if (mInitialized)
	{
		// Calculate new distance of the camera
		//if (abs(mpTrackedObject->getBody()->getLinearVelocity().length()) > 0.01f)
		//{
		//	mfLastDistance += 0.1f;
		//	if (mfLastDistance > mfMaxDistance) mfLastDistance = mfMaxDistance;
		//}
		//else
		//{
		//	mfLastDistance -= 0.07f;
		//	if (mfLastDistance < mfMinDistance) mfLastDistance = mfMinDistance;
		//}


		// Calculate new position of the camera
		Ogre::Matrix3 objRotation;
		mpTrackedObject->getNode()->getOrientation().ToRotationMatrix(objRotation);

		Ogre::Vector3 lookAtZ(objRotation * Ogre::Vector3(0.0f, 0.0f, 1.0f));
		Ogre::Vector3 lookAtCam = mpCamera->getPosition() - cvt(mpTrackedObject->getBody()->getWorldTransform().getOrigin());
		lookAtCam.normalise();

		//Ogre::Vector3 lookAtZ2 = lookAtZ;
		//for (std::list<const Ogre::Vector3>::const_iterator it = mLookAtZList.begin();
		//	 it != mLookAtZList.end(); ++it)
		//{
		//	//lookAtZ += *it;
		//}
		////lookAtZ /= 3.0f;
		//lookAtZ.normalise();
		//mLookAtZList.push_back(lookAtZ);
		//mLookAtZList.pop_front();
	
		Ogre::Quaternion q;
		Ogre::Vector3 newLookAtCam;

		q = lookAtCam.getRotationTo(lookAtZ);
		if (!mReseted) q = q * 0.3f;
		newLookAtCam = q * lookAtCam;

		newLookAtCam.normalise();
		newLookAtCam = newLookAtCam * mfLastDistance;

		mpCamera->setPosition(cvt(mpTrackedObject->getBody()->getWorldTransform().getOrigin()) + newLookAtCam);
		

		// Calculate new orientation around x-axis of the camera
		Ogre::Matrix3 camRotation;
		mpCamera->getOrientation().ToRotationMatrix(camRotation);

		Ogre::Vector3 camXAxis(camRotation * Ogre::Vector3(1.0f, 0.0f, 0.0f));
		Ogre::Vector3 charXAxis(objRotation * Ogre::Vector3(1.0f, 0.0f, 0.0f));

		if (!mReseted)
		{
			for (std::list<const Ogre::Vector3>::const_iterator it = mCharXAxisList.begin();
				 it != mCharXAxisList.end(); ++it)
			{
				charXAxis += *it;
			}
			charXAxis /= 5.0f;
			charXAxis.normalise();
			mCharXAxisList.push_back(charXAxis);
			mCharXAxisList.pop_front();

			q = camXAxis.getRotationTo(charXAxis);
			q = q * 0.2f;
		}
		else
		{
			q = camXAxis.getRotationTo(charXAxis);
		}
		mpCamera->rotate(q);

		
		// Calculate new orientation around y-axis of the player
		mpCamera->getOrientation().ToRotationMatrix(camRotation);
		Ogre::Vector3 camZAxis(camRotation * Ogre::Vector3(0.0f, 0.0f, 1.0f));
		lookAtCam = mpCamera->getPosition() - cvt(mpTrackedObject->getBody()->getWorldTransform().getOrigin());
		lookAtCam.normalise();

		if (!mReseted)
		{
			float f = 0.1f;
			for (std::list<const Ogre::Vector3>::const_iterator it = mLookAtCamList.begin();
				 it != mLookAtCamList.end(); ++it)
			{
				lookAtCam += (*it);
				f *= 2.0f;
			}
			lookAtCam.normalise();
			mLookAtCamList.push_back(lookAtCam);
			mLookAtCamList.pop_front();

			q = camZAxis.getRotationTo(lookAtCam);
			if (!mReseted) q = q * 0.5f;
		} else q = camZAxis.getRotationTo(lookAtCam);
		mpCamera->rotate(q);
		
		mReseted = false;
	}
}
//-------------------------------------------------------------------------------------
void GameCamera::reset(void)
{
	mCharXAxisList.clear();
	mLookAtCamList.clear();

	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mCharXAxisList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	mLookAtCamList.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f));

	mReseted = true;
}
//-------------------------------------------------------------------------------------
void GameCamera::updateAspectRatio(void)
{
	if (mInitialized)
	{
		mpCamera->setAspectRatio(Ogre::Real(mpViewport->getActualWidth()) / Ogre::Real(mpViewport->getActualHeight()));
	}
}
//-------------------------------------------------------------------------------------
Ogre::Camera *GameCamera::getCamera(void) const
{
	return mpCamera;
}
//-------------------------------------------------------------------------------------
float GameCamera::getMinDistance(void) const
{
	return mfMinDistance;
}
//-------------------------------------------------------------------------------------
float GameCamera::getMaxDistance(void) const
{
	return mfMaxDistance;
}
//-------------------------------------------------------------------------------------
void GameCamera::setMinDistance(const float distance)
{
	mfMinDistance = distance;
}
//-------------------------------------------------------------------------------------
void GameCamera::setMaxDistance(const float distance)
{
	mfMaxDistance = distance;
}